#!/usr/bin/env python3
# Copyright (c) 2020-2021 maminjie <maminjie1@huawei.com>
# SPDX-License-Identifier: MulanPSL-2.0

"""
This is openEuler source counter script
"""

import sys
import os
import argparse
import re
import codecs

base_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../..")
if base_path not in sys.path:
    sys.path.append(base_path)

from oec.apps import codestat
from oec.apps.common import *

class OeCodeStat(object):
    """
    openEuler code statistics
    """
    def _format_review_body(self, body):
        return re.sub("\"", "", body)

    def _format_review_data(self, review_dict, separator):
        data = "{}:{}{}".format(review_dict.get("name"),
                self._format_review_body(review_dict.get("body")),
                separator)
        return data

    def _get_new_filename(self, oldname, num):
        if num == 0:
            return oldname
        (path, filename) = os.path.split(oldname)
        namelist = filename.split(".")
        basename = namelist[0]
        basename += "_" + str(num)
        if path:
            newname = path + "/" + basename
        else:
            newname = basename
        if len(namelist) > 1:
            for suffix in namelist[1:]:
                newname += "." + suffix
        return newname

    def _format_merge_statistics(self, stat):
        data = "{},{},{},{},{},{},\"{}\",{},{},{},+{},-{},{},{}".format(stat["user"], stat["repo"], stat["branch"],
            stat["state"], stat["prNum"], stat["prType"], stat["prTitle"], stat["prAuthor"], stat["failCnt"], stat["rejectCnt"],
            stat["addition"], stat["deletion"], stat["total"], stat["reviewCnt"])
        reviewer_list = stat.get("reviewer")
        if reviewer_list:
            data += ",\""
            list_len = len(reviewer_list)
            if list_len == 1:
                review_dict = reviewer_list[0]
                data += self._format_review_data(review_dict, "\"")
            elif list_len == 2:
                review_dict = reviewer_list[0]
                data += self._format_review_data(review_dict, "\n")
                review_dict = reviewer_list[1]
                data += self._format_review_data(review_dict, "\"")
            else:
                for review_dict in reviewer_list[0:-1]:
                    data += self._format_review_data(review_dict, "\n")
                review_dict = reviewer_list[-1]
                data += self._format_review_data(review_dict, "\"")
            data += ",{}".format(stat["time"])
        return data

    def merge_statistics(self, user_list, repo_list, owner, repo_type, branch, start_time, end_time, outfile):
        max_lines = 3000
        line_counter = 0
        file_num = 0
        fp = None
        header = "{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}".format("user", "repo", "branch",
            "state", "prNo.", "prType", "prTitle", "prAuthor", "failCnt", "rejectCnt",
            "addition", "deletion", "total", "reviewCnt", "reviewer", "time")
        code_stat = codestat.CodeStat()
        all_repo_flag = False
        if not repo_list:
            all_repo_flag = True

        for user in user_list:
            if all_repo_flag:
                page = 1
                repo_list_tmp = code_stat.get_org_repos(owner, repo_type=repo_type, page=page)
            else:
                repo_list_tmp = repo_list
            while len(repo_list_tmp):
                for repo in repo_list_tmp:
                    print("{user}:{repo}".format(user=user, repo=repo))
                    stats = code_stat.merge_statistics(user, owner, repo, branch, start_time, end_time)
                    if not stats:
                        continue
                    for stat in stats:
                        data = self._format_merge_statistics(stat)
                        if not fp:
                            fp = codecs.open(self._get_new_filename(outfile, file_num), mode='w', encoding="utf_8_sig")
                            if not fp:
                                return False
                            print(header)
                            fp.write("{}\n".format(header))
                        print(data)
                        fp.write("{}\n".format(data))
                        fp.flush()
                        line_counter += 1
                        if line_counter % max_lines == 0:
                            file_num += 1
                            if fp:
                                fp.close()
                                fp = None
                if all_repo_flag:
                    page += 1
                    repo_list_tmp = code_stat.get_org_repos(owner, repo_type=repo_type, page=page)
                else:
                    break
        if fp:
            fp.close()
        return True

    def contribute_statistics(self, user_list, repo_list, owner, repo_type, branch, start_time, end_time, outfile):
        fp = codecs.open(outfile, mode='w', encoding="utf_8_sig")
        if not fp:
            return False
        header = "{},{},{},{},{},{},{},{},{}".format(
            "user", "commitCnt", "mergeCnt", "rejectCnt", "failCnt", "addition", "deletion", "total", "reviewCnt")
        print(header)
        fp.write("{}\n".format(header))

        code_stat = codestat.CodeStat()
        all_repo_flag = False
        if not repo_list:
            all_repo_flag = True

        for user in user_list:
            commitCnt = 0
            mergeCnt = 0
            rejectCnt = 0
            failCnt = 0
            addition = 0
            deletion = 0
            total = 0
            reviewCnt = 0
            if all_repo_flag:
                page = 1
                repo_list_tmp = code_stat.get_org_repos(owner, repo_type=repo_type, page=page)
            else:
                repo_list_tmp = repo_list

            while len(repo_list_tmp):
                for repo in repo_list_tmp:
                    print("{user}:{repo}".format(user=user, repo=repo))
                    stat = code_stat.contribute_statistics(user, owner, repo, branch, start_time, end_time)
                    if not stat:
                        continue
                    commitCnt += stat["commitCnt"]
                    mergeCnt += stat["mergeCnt"]
                    rejectCnt += stat["rejectCnt"]
                    failCnt += stat["failCnt"]
                    addition += stat["addition"]
                    deletion += stat["deletion"]
                    total += stat["total"]
                    reviewCnt += stat["reviewCnt"]
                    data = "{},{},{},{},{},+{},-{},{},{}".format(user, commitCnt, mergeCnt, rejectCnt,
                        failCnt, addition, deletion, total, reviewCnt)
                    print(data)
                if all_repo_flag:
                    page += 1
                    repo_list_tmp = code_stat.get_org_repos(owner, repo_type=repo_type, page=page)
                else:
                    break
            data = "{},{},{},{},{},+{},-{},{},{}".format(user, commitCnt, mergeCnt, rejectCnt,
                failCnt, addition, deletion, total, reviewCnt)
            print(data)
            fp.write("{}\n".format(data))
            fp.flush()

        fp.close()
        return True


def check_time(start, end):
    if start != "" and end != "" and start > end:
        print("The endtime {} should be greater than or equal to the starttime {}".format(end, start))
        return False
    return True


def parse_command_line():
    params = argparse.ArgumentParser()
    params.add_argument("-c", "--command", type=str, choices=['merge', 'contribute'], required=True,
                        help="The subcommand, as follows: merge, contribute")
    params.add_argument("-u", "--user", type=str,
                        help="The username file or list")
    params.add_argument("-r", "--repo", type=str, default="",
                        help="The repository file or list, default is all repos")
    params.add_argument("-t", "--repotype", type=str, default="all",
                        help="The repository type, as follows: all/public/private, default is all")
    params.add_argument("-b", "--branch", type=str, default="all",
                        help="The branch name, default is all branch")
    params.add_argument("-p", "--project", type=str, default="src-openeuler",
                        help="The project name, default is src-openeuler")
    params.add_argument("-s", "--starttime", type=str, default="",
                        help="The start time, format is ISO 8601, default is not set")
    params.add_argument("-e", "--endtime", type=str, default="",
                        help="The end time, format is ISO 8601, default is not set")
    params.add_argument("-o", "--outfile", type=str, default="",
                        help="The output file, default is {command}_stat_result.csv")
    args = params.parse_args()
    return args


def do_main(args):
    if not args.command or not args.user:
        print("args invalid, please use -h for help")
        sys.exit(1)

    if args.command != "merge" and args.command != "contribute":
        print("command invalid, please use -h for help")
        sys.exit(1)

    if not check_time(args.starttime, args.endtime):
        sys.exit(1)

    user_list = get_list(args.user)
    if not user_list:
        print("user list invalid")
        sys.exit(1)

    repo_list = []  # empty means all
    if args.repo != "":
        repo_list = get_list(args.repo)
        if not repo_list:
            print("repo list invalid")
            sys.exit(1)

    if not args.outfile:
        outfile = "{}_stat_result.csv".format(args.command)
    else:
        outfile = args.outfile

    oe_codestat = OeCodeStat()
    if args.command == "merge":
        oe_codestat.merge_statistics(user_list, repo_list, args.project, args.repotype,
                args.branch, args.starttime, args.endtime, outfile)
    elif args.command == "contribute":
        oe_codestat.contribute_statistics(user_list, repo_list, args.project, args.repotype,
                args.branch, args.starttime, args.endtime, outfile)


def main():
    args = parse_command_line()
    do_main(args)


if __name__ == "__main__":
    main()
